package uml2Java;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.emf.common.util.EList;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.DataType;
import org.eclipse.uml2.uml.Enumeration;
import org.eclipse.uml2.uml.Interface;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.VisibilityKind;

import astjava.AbstractTypeDeclaration;
import astjava.InheritanceKind;
import astjava.JavaFactory;
import astjava.Model;
import astjava.ParameterizedType;
import astjava.PrimitiveType;
import astjava.UnresolvedTypeDeclaration;

public class TransformPackagesAndTypes {

	private Package _umlPackage; 
	
	private Model _javaModel;
	
	private Map<String, astjava.Type> _symbolMap;
	
	public Package get_umlPackage() {
		return _umlPackage;
	}

	public Model get_javaModel() {
		return _javaModel;
	}
	
	public Map<String,astjava.Type> get_symbolMap() {
		return _symbolMap;
	}
	
	public TransformPackagesAndTypes(Package umlPackage, Model javaModel) {
		_javaModel = javaModel;
		_umlPackage = umlPackage;
		_symbolMap = new HashMap<String, astjava.Type>();
	}

	//doTransformPackagesAndTypes
	public void doTransformPackagesAndTypes() {
		_javaModel.getOwnedElements().add(transformPackage(_umlPackage));
		generatePrimitiveType();
		transformTypes(_umlPackage, _javaModel.getOwnedElements().get(0));
	}

	private void transformTypes(Package umlPackage, astjava.Package javaPackage) {
		EList<Type> UMLTypes = umlPackage.getOwnedTypes();
		for (Type t : UMLTypes) {
			astjava.AbstractTypeDeclaration javaType = null;
			try {
				if (t instanceof Class) 
					javaType = transformClass((Class) t);
									
				else if (t instanceof Interface) {
					javaType = transformInterface((Interface) t);
				}
				else if (t instanceof Enumeration) {
					javaType = transformEnumeration((Enumeration) t);
				}
				else if (t instanceof DataType) {
					javaType = transformDataType((DataType) t);
				}	
				else if (t instanceof Association) {
					
				}
				else {
					System.err.println("Type non supporté" + t);
				}
			} catch (IllegalVisibityException e) {
				e.printStackTrace();
			}
			if (javaType != null) {
				javaPackage.getOwnedElements().add(javaType);
				_symbolMap.put(t.getName(), javaType);
			}
		}
	}

	private AbstractTypeDeclaration transformDataType(DataType t) {
		UnresolvedTypeDeclaration dataTypeDeclaration = JavaFactory.eINSTANCE.createUnresolvedTypeDeclaration();
		dataTypeDeclaration.setName(t.getName());
		return dataTypeDeclaration;
	}


	private AbstractTypeDeclaration transformEnumeration(Enumeration t) throws IllegalVisibityException {
		validateVisibilityOnType(t);
		astjava.EnumDeclaration javaEnumeration = JavaFactory.eINSTANCE.createEnumDeclaration();
		setType(t, javaEnumeration);
		return javaEnumeration;
	}

	private AbstractTypeDeclaration transformInterface(Interface t) throws IllegalVisibityException {
		validateVisibilityOnType(t);
		astjava.InterfaceDeclaration javaInterface = JavaFactory.eINSTANCE.createInterfaceDeclaration();
		setType(t, javaInterface);
		return javaInterface;
	}

	private AbstractTypeDeclaration transformClass(Class t) throws IllegalVisibityException {
		validateVisibilityOnType(t);
		astjava.ClassDeclaration javaClass = JavaFactory.eINSTANCE.createClassDeclaration();
		setType(t, javaClass);
		if (t.isAbstract())
			javaClass.getModifier().setInheritance(InheritanceKind.ABSTRACT);
		return javaClass;
	}
	
	private void validateVisibilityOnType(Type t) throws IllegalVisibityException {
		if (t.getVisibility().equals(VisibilityKind.PRIVATE_LITERAL) ||
				t.getVisibility().equals(VisibilityKind.PROTECTED_LITERAL)) {
			throw new IllegalVisibityException(t);
		}
	}
	
	private void setType(Type t, astjava.AbstractTypeDeclaration javaType) {
		javaType.setName(t.getName());
		javaType.setModifier(JavaFactory.eINSTANCE.createModifier());
		javaType.getModifier().setVisibility(transformVisibility(t.getVisibility()));
	}

	astjava.VisibilityKind transformVisibility(VisibilityKind umlVisibility) {
		if (umlVisibility.equals(VisibilityKind.PUBLIC_LITERAL))
			return astjava.VisibilityKind.PUBLIC;
		if (umlVisibility.equals(VisibilityKind.PACKAGE_LITERAL))
			return astjava.VisibilityKind.NONE;
		if (umlVisibility.equals(VisibilityKind.PROTECTED_LITERAL))
			return astjava.VisibilityKind.PROTECTED;
		if (umlVisibility.equals(VisibilityKind.PRIVATE_LITERAL))
			return astjava.VisibilityKind.PRIVATE;
		return null;
	}
	
	private void generatePrimitiveType() {
		PrimitiveType typePrimitif = JavaFactory.eINSTANCE.createPrimitiveTypeBoolean();
		typePrimitif.setName("boolean");
		_javaModel.getOrphanTypes().add(typePrimitif);
		_symbolMap.put("EBoolean", typePrimitif);
		
		typePrimitif = JavaFactory.eINSTANCE.createPrimitiveTypeByte();
		typePrimitif.setName("byte");
		_javaModel.getOrphanTypes().add(typePrimitif);
		_symbolMap.put("EByte", typePrimitif);
		
		typePrimitif = JavaFactory.eINSTANCE.createPrimitiveTypeChar();
		typePrimitif.setName("char");
		_javaModel.getOrphanTypes().add(typePrimitif);
		_symbolMap.put("EChar", typePrimitif);

		typePrimitif = JavaFactory.eINSTANCE.createPrimitiveTypeDouble();
		typePrimitif.setName("double");
		_javaModel.getOrphanTypes().add(typePrimitif);
		_symbolMap.put("EDouble", typePrimitif);
		
		typePrimitif = JavaFactory.eINSTANCE.createPrimitiveTypeFloat();
		typePrimitif.setName("float");
		_javaModel.getOrphanTypes().add(typePrimitif);
		_symbolMap.put("EFloat", typePrimitif);
		
		typePrimitif = JavaFactory.eINSTANCE.createPrimitiveTypeInt();
		typePrimitif.setName("int");
		_javaModel.getOrphanTypes().add(typePrimitif);
		_symbolMap.put("EInt", typePrimitif);
		
		typePrimitif = JavaFactory.eINSTANCE.createPrimitiveTypeLong();
		typePrimitif.setName("long");
		_javaModel.getOrphanTypes().add(typePrimitif);
		_symbolMap.put("ELong", typePrimitif);
		
		typePrimitif = JavaFactory.eINSTANCE.createPrimitiveTypeShort();
		typePrimitif.setName("short");
		_javaModel.getOrphanTypes().add(typePrimitif);
		_symbolMap.put("EShort", typePrimitif);
		
		typePrimitif = JavaFactory.eINSTANCE.createPrimitiveType();
		typePrimitif.setName("String");
		_javaModel.getOrphanTypes().add(typePrimitif);
		_symbolMap.put("EString", typePrimitif);
		
		typePrimitif = JavaFactory.eINSTANCE.createPrimitiveTypeVoid();
		typePrimitif.setName("void");
		_javaModel.getOrphanTypes().add(typePrimitif);
		_symbolMap.put("EVoid", typePrimitif);
		
		ParameterizedType typeCollection = JavaFactory.eINSTANCE.createParameterizedType();
		typeCollection.setName("Collection");
		_javaModel.getOrphanTypes().add(typeCollection);
		_symbolMap.put("Collection", typeCollection);
	}

	private astjava.Package transformPackage(Package umlPackage) {
		astjava.Package javaPackage = JavaFactory.eINSTANCE.createPackage();
		javaPackage.setName(_umlPackage.getName());
		return javaPackage;
	}



}
